/*
 * @Descripttion: 驼峰与下划线结构处理
 * @version: 0.0.0.1
 * @Author: chenyf15482
 * @Date: 2022-12-25 15:35:01
 * @LastEditors: chenyf15482
 * @LastEditTime: 2022-12-27 10:34:46
 */

import { memoize, isArray, isMap, isSet, isPlainObject } from 'lodash';

// 默认数据类型
type yAny = any
type transFunc = (s: string) => string;

/**
 * @msg 转驼峰结构风格
 *
 * @param {String}
 * @return {String}
 */
function _toCamelCase(str: string): string {
    return str.replace(/_(\w)/g, (_, letter) => letter.toUpperCase());
}

/**
 * @msg 转小写下划线结构风格
 *
 * @param {String}
 * @return {String}
 */
function _toUnderscoreCase(str: string): string {
    return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}

/**
 * @msg 将数组中的key值转换对应的结构风格 并检查value是否需要进一步转换
 * @param {Array} arr [['egOne', 1], ['eg_two', { a: 1}], ['eg_three', [{a: 1}]]]
 * @param {transFunc} to 结构风格转换函数
 * @return {*}
 */
function _transKey(arr: Array<Array<yAny>>, to: transFunc) {
    const result = arr.map(([key, value]) => [to(key), _judgeAndDeal(value, to)]);
    return result;
}

const _toCamelCaseMem = memoize(_toCamelCase);
const _toUnderscoreCaseMem = memoize(_toUnderscoreCase);

/**
 * @msg 处理object对象类型的数据
 * @param {object} obj { egOne: 1, eg_two: {a: 1}}
 * @return {*}
 */
function _dealObject(obj: object, to: transFunc) {
    // 调用转换函数
    const result = _transKey(Object.entries(obj), to);
    // 还原对象并返回处理结果
    return result.reduce((o, [k, v]) => Object.assign(o, { [k]: v}), {})
}

/**
 * @msg 处理array数组类型的数据
 * @param {Array} arr
 * @return {*}
 */
function _dealArray(arr: Array<yAny>, to: transFunc) {
    const result: yAny[] = [];
    arr.forEach((row) => {
        // 对每一项数据进行检查和处理
        const record = _judgeAndDeal(row, to);
        // 将处理结果还原成数组
        result.push(record);
    });
    return result;
}

/**
 * @msg 基本数据类型判断处理
 *  主要为 number | string | boolean | array | object
 *  不需要考虑void any unknown never enum tuple null undefined等
 * @param {yAny} value
 * @return {*}
 */
function _judgeAndDeal(value: yAny, to: transFunc) {
    // typeof：object undefined string number boolean function
    let result = value;
    if (value && typeof value === 'object') {
        // 进一步判断 array 等类型
        if (isArray(value)) {
            result = _dealArray(value, to);
        } else if (isPlainObject(value)) {
            result = _dealObject(value, to);
        } else if (isMap(value)) {
            // 类似于对象  待补充处理
        } else if (isSet(value)) {
            // 类似于数组  待补充处理
        }
    }
    return result;
}

/**
 * @msg 转驼峰结构
 * @param {yAny} data
 * @return {*}
 */
function _transKeyToCamelCase(data: yAny) {
    return _judgeAndDeal(data, _toCamelCaseMem);
}

/**
 * @msg 转下划线结构
 * @param {yAny} data
 * @return {*}
 */
function _transKeyToUnderScoreCase(data: yAny) {
    return _judgeAndDeal(data, _toUnderscoreCaseMem);
}

function _transKeyTo(data: yAny, keyType: yFieldKeyType) {
    let result = data;
    switch (keyType) {
        case 'camel':
            result = _transKeyToCamelCase(data);
            break;
        case 'underscore':
            result = _transKeyToUnderScoreCase(data);
            break;
        default:
            break;
    }
    return result;
}

export { _transKeyToCamelCase, _transKeyToUnderScoreCase, _transKeyTo };
export default { _transKeyToCamelCase, _transKeyToUnderScoreCase, _transKeyTo };
